 /*
 * SceneModule.h
 *
 * Created 11/30/2008 By Johnny Huynh
 *
 * Version 00.00.01 11/30/2008
 *
 * Copyright Information:
 * All content copyright © 2008 Johnny Huynh. All rights reserved.
 */
 
 // SceneModule.h handles all of the rendering and display (including the setups)

 #ifndef SCENE_MODULE_H
 #define SCENE_MODULE_H
 
 #include "OpenGL_Headers.h"

 #include <iostream>
 #include "Extract_Pts.h"

 #include "GL_Actor.h"

 void RenderScene( void );
 void ChangeSize( int w, int h );
 void TimerFunction( int timer );
 void SetupRC( void );
 void InitializeCallBacks();
 void enterFullScreenMode();
 void enterWindowMode();
 
 #include "GL_Object.h"
 void testRenderObject();



#include "InterfaceModule.h"
#include "InputModule.h"    // for keyboard and mouse interactions

#define CIRCLERX 0.0f
#define CIRCLERY -4.0f
#define CIRCLERZ 5.0f

static GLint windowID = 0x0;    // Tracks the ID of the windows opened in order to close the windows
static std::vector<Vector3f> horse_points;

//#define OLD
#define NEW

void RenderScene( void )
{
    static GLfloat maxRewindTime = 0.0f;
    
	static GLint stablizeTimer = 200;

	if ( stablizeTimer != 0 )
		--stablizeTimer;
	else
	{
		applyKeyboardControl();
		applyMouseControl();
	}
    
	Vector3f cam_position( 0.0f, 0.0f, 0.0f );//cam.getFaceAxis() );
	GLfloat positionLight0[] = { cam_position.x, cam_position.y, cam_position.z, 1.0f };
	glLightfv(GL_LIGHT0, GL_POSITION, positionLight0);

	Vector3f cam_dir( 0.0f, 0.0f, -1.0f );//cam.getFaceAxis() );
	GLfloat spotDirectionLight0[] = { cam_dir.x, cam_dir.y, cam_dir.z };
	glLightfv(GL_LIGHT0, GL_SPOT_DIRECTION, spotDirectionLight0);

    // WINDOWS Specific
    if ( isGameMode )
    {
        ShowCursor( false );
        SetCursorPos( windowWidth/0x2, windowHeight/0x2 );
    }
    else
    {
        ShowCursor( true );
    }
    // END of WINDOWS Specific code
    
    //static GLfloat rotateAngle = 0.0f;
    
    glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );

    glPushMatrix();

        cam.transformCamera();

		++global::elapsedFrame;
        if ( !PAUSE )
        {
            #ifdef NEW
            // Set displacement for Mobj circler
            objects[0]->setPosition( 0.0f, 0.0f, 0.0f );
            objects[0]->globalRotateUp( global::time*0.07f*global::speed_factor );
            const Vector3f vFace2 = objects[0]->getFaceAxis();
            objects[0]->setPosition( CIRCLERX + 7*vFace2.x, CIRCLERY + 7*vFace2.y, CIRCLERZ + 7*vFace2.z );
			// CS 211A Assignment #2
			objects[2]->localRotateLeft( global::time*0.035f*global::speed_factor );
			const Vector3f vCenter2 = objects[2]->getPosition();
			const Vector3f vFace3 = objects[2]->getFaceAxis();
			objects[3]->setPosition( vCenter2.x + 10.0f*vFace3.x, vCenter2.y + 10.0f*vFace3.y, vCenter2.z + 10.0f*vFace3.z );
            #endif
            
            #ifdef NEW
            //++global::elapsedFrame;
            clock_t currentTime = clock();
            if ( ((currentTime - global::elapsedTime) / CLOCKS_PER_SEC) >= 1.0 )
            {
                global::fps = (((GLfloat)global::elapsedFrame)/(currentTime - global::elapsedTime)) * CLOCKS_PER_SEC;
                global::speed_factor = global::base_fps / global::fps;
				global::speed_factor = MIN( global::speed_factor, 1.0f );
                printf( "%1f\n", global::fps );
                //std::cout << "CurrentTime: " << currentTime << std::endl;
                //std::cout << "ElapsedTime: " << global::elapsedTime << std::endl;
                global::elapsedTime = currentTime;
                global::elapsedFrame = 0;
            }
            
            // For collision interpolation of the user controlled object
            if ( !hasFiredHitBox )
            {
                    //objects[ objects.size() - 1 ]->setPreviousPosition( cam.getPosition() );
                    maintainMobj();
            }
            
            for ( size_t i = 0x0; i < objects.size(); ++i )
            {
                checkCollision<GLfloat>( *objects[i], ((GL_Objectf**)(&objects[i] + 1)), objects.size() - i - 1 );
            }
            #endif
            
            #ifdef NEW
            // Update displacement for Mobj
            for ( size_t i = 0x0; i < objects.size(); ++i )
            {
                if ( i > 0x2 )//&& (i != objects.size() - 1 || hasFiredHitBox) )
                    objects[i]->updateDisplacement( global::time*global::speed_factor );
                    
            }
            #endif
        }
        
        #ifdef NEW
        // Render
        for ( GLint i = 0x0; i < (GLint) objects.size(); ++i )
        {
            if ( i == 0x0 )
                glColor3f( 1.0f, 0.0f, 0.0f );
            else if ( i == 0x1 )
                glColor3f( 0.0f, 1.0f, 1.0f );
            // Walls
            else if ( i == 0x2 )
                glColor3f( 0.4f, 0.4f, 0.5f );
            else if ( i == 0x3 )
                glColor3f( 0.4f, 0.3f, 0.0f );
            else
                glColor3f( 0.5f, 0.5f, 0.0f );
                
            render( *(objects[i]), OBBTree_depth_level );
        }

		// Render Horse Points
		/*size_t size( horse_points.size() );
		for ( size_t i(0x0); i < size; ++i )
		{
			Vector3f v( horse_points[ i ] );
			glPushMatrix();
				glColor3f( 0.0f, 1.0f, 0.0f );
				glTranslatef( v.x, v.y, v.z );
				glutSolidSphere( horse_sphere_radius, 15, 15);
			glPopMatrix();
		}*/
        #endif
    glPopMatrix();
    
    //if ( isGameMode )
    {
        // Draw reticle
        glPushMatrix();
            glColor3f( 1.0f, 1.0f, 1.0f );
            glTranslatef( 0.0f, 0.0f, -1.0f );
            glutSolidSphere( 0.005f, 15, 15 );
        glPopMatrix();
        // End of draw reticle
    }

    glutSwapBuffers();
}

void ChangeSize( int w, int h )
{
    // For mouse pointer
    windowWidth = w;
    windowHeight = h;
    // end of For mouse pointer

    if ( w == 0 )
        w = 1;
    if ( h == 0 )
        h = 1;
    
    glViewport( 0, 0, w, h );
    
    glMatrixMode( GL_PROJECTION );
    glLoadIdentity();
    
    GLfloat aspectRatio = (GLfloat)w / (GLfloat)h;
    
    gluPerspective( VIEW_ANGLE, aspectRatio, 0.1, 400.0 );
    
    glMatrixMode( GL_MODELVIEW );
    glLoadIdentity();
}

void TimerFunction( int timer )
{
    glutPostRedisplay();
    
    glutTimerFunc( timer, TimerFunction, timer );
}

// Setup Rendering Context
void SetupRC( void )
{
    #ifdef NEW
    static GL_MobileObjectf* redmo = 0x0;
    if ( redmo == 0x0 )
    {
        Vector3f velocity( 0.0, 0.0, 0.0 );
        Vector3f location( CIRCLERX, CIRCLERY, CIRCLERZ );
        
        objects.push_back( new GL_MobileObjectf( *box ) );
        
        location = Vector3f( 5.0f, 0.5f, 10.0f );
        
        GLfloat width = 3.0f; // 5.0f
        GLfloat height = 3.0f; // 4.0f
        GLfloat depth = 3.0f; // 3.0f
        
        Vector3f vecF( -width, -height, -depth ); // bottom-back-left
        Vector3f vecG( width, -height, -depth ); // bottom-back-right
        Vector3f vecH( -width, height, -depth ); // top-back-left
        Vector3f vecI( width, height, -depth ); // top-back-right

        Vector3f vecJ( -width, -height, depth ); // bottom-front-left
        Vector3f vecK( width, -height, depth ); // bottom-front-right
        Vector3f vecL( -width, height, depth ); // top-front-left
        Vector3f vecM( width, height, depth ); // top-front-right

        GL_Trianglef triM( vecF, vecH, vecI ); // back-top
        GL_Trianglef triN( vecI, vecG, vecF ); // back-bottom
         
        GL_Trianglef triO( vecM, vecL, vecJ ); // front-top
        GL_Trianglef triP( vecJ, vecK, vecM ); // front-bottom
         
        GL_Trianglef triQ( vecL, vecH, vecF ); // left-top
        GL_Trianglef triR( vecF, vecJ, vecL ); // left-bottom
        
        GL_Trianglef triS( vecG, vecI, vecM ); // right-top
        GL_Trianglef triT( vecM, vecK, vecG ); // right-bottom
        
        GL_Trianglef triU( vecL, vecM, vecI ); // top-front
        GL_Trianglef triV( vecI, vecH, vecL ); // top-back
        
        GL_Trianglef triW( vecG, vecK, vecJ ); // bottom-front
        GL_Trianglef triX( vecJ, vecF, vecG ); // bottom-back
       
        /*GL_Trianglef triM( vecI, vecH, vecF ); // back-top
        GL_Trianglef triN( vecF, vecG, vecI ); // back-bottom
         
        GL_Trianglef triO( vecJ, vecL, vecM ); // front-top
        GL_Trianglef triP( vecM, vecK, vecJ ); // front-bottom
         
        GL_Trianglef triQ( vecF, vecH, vecL ); // left-top
        GL_Trianglef triR( vecL, vecJ, vecF ); // left-bottom
        
        GL_Trianglef triS( vecM, vecI, vecG ); // right-top
        GL_Trianglef triT( vecG, vecK, vecM ); // right-bottom
        
        GL_Trianglef triU( vecI, vecM, vecL ); // top-front
        GL_Trianglef triV( vecL, vecH, vecI ); // top-back
        
        GL_Trianglef triW( vecJ, vecK, vecG ); // bottom-front
        GL_Trianglef triX( vecG, vecF, vecJ ); // bottom-back */
        
        std::vector< GL_Trianglef > triangles;
        triangles.push_back( triM );
        triangles.push_back( triN );
        triangles.push_back( triO );
        triangles.push_back( triP );
        triangles.push_back( triQ );
        triangles.push_back( triR );
        triangles.push_back( triS );
        triangles.push_back( triT );
        triangles.push_back( triU );
        triangles.push_back( triV );
        triangles.push_back( triW );
        triangles.push_back( triX );
        objects.push_back( new GL_MobileObjectf( *box, location ) );
		
        location = Vector3f(10.0f, CIRCLERY, -20.0f);
        //location = Vector3f( -3.0f, -2.0f, 10.0f );
        objects.push_back( new GL_MobileObjectf( triangles, location ) );
		// smaller_box
		location.z = -30.0f;
		for (size_t i = 0; i < triangles.size(); ++i)
		{
			Vector3<GLfloat> new_center( triangles[i].getCenter() * 0.25f );
			triangles[i] = apply( triangles[i], new_center - triangles[i].getCenter() )*0.25f;
		}
		objects.push_back( new GL_MobileObjectf( triangles, location ) );
        
        location = Vector3f(10.0f, CIRCLERY, 20.0f);
        objects.push_back( new GL_MobileObjectf( *convex, location ) );
        
       /* width = 5.0f;
        height = 1.0f;
        depth = 5.0f;
        
        Vector3f vecA( -width, height, -depth ); // top-back-left
        Vector3f vecB( width, height, -depth ); // top-back-right
        Vector3f vecC( -width, height, depth ); // top-front-left
        Vector3f vecD( width, height, depth ); // top-front-right
        //Vector3f vecE( 0.0f, 0.5f, 0.0f );
        
        GL_Trianglef triA( vecC, vecD, vecB );
        GL_Trianglef triB( vecB, vecA, vecC );
        //GL_Trianglef triC( vecD, vecE, vecA );
     
     
     
        triangles.clear();
        triangles.push_back( triA );
        triangles.push_back( triB );
        //triangles.push_back( triC );*/
        redmo = new GL_MobileObjectf( triangles, 0.0f, CIRCLERY - 10.0f, 0.0f );
        //objects.push_back( redmo );
        /*redHB = new HitBox( 4.0, 3.0, 5.0, velocity, location );
        hb.push_back( redHB );
        hb.push_back( new HitBox( 1.0, 1.0, 1.0, velocity, location ) );
        
        // bottom
        location[0] = 0.0f;
        location[1] = CIRCLERY - 10.0f;
        location[2] = 0.0f;
        HitBox* wall = new HitBox( 150.0, .5, 150.0, velocity, location );
        hb.push_back( wall );
        
        // top
        location[1] = CIRCLERY + 76.0f;
        wall = new HitBox( 150.0, .5, 150.0, velocity, location );
        hb.push_back( wall );
        
        // right
        location[0] = 150.0f;
        location[1] = CIRCLERY + 33.0f;
        location[2] = 0.0f;
        wall = new HitBox( .5, 43.0, 150.0, velocity, location );
        hb.push_back( wall );
        
        // left
        location[0] = -150.0f;
        wall = new HitBox( .5, 43.0, 150.0, velocity, location );
        hb.push_back( wall );
        
        // front
        location[0] = 0.0f;
        location[1] = CIRCLERY + 33.0f;
        location[2] = 75.0f;
        wall = new HitBox( 150.0, 43.0, .5, velocity, location );
        hb.push_back( wall );
        
        // back
        location[2] = -75.0f;
        wall = new HitBox( 150.0, 43.0, .5, velocity, location );
        hb.push_back( wall );*/
    }
    #endif // NEW

    glColor3f( 1.0f, 0.0f, 0.0f );
    glClearColor( 0.0f, 0.0f, 0.0f, 0.2f );

    glEnable( GL_DEPTH_TEST );
    glEnable( GL_CULL_FACE );
    glFrontFace( GL_CCW );
    glCullFace( GL_BACK );
    
    GLfloat ambientLight[] = { 1.0f, 1.0f, 1.0f, 1.0f };
    
    glEnable( GL_LIGHTING );
    //glLightModelfv( GL_LIGHT_MODEL_AMBIENT, ambientLight );
    
    glEnable( GL_COLOR_MATERIAL );
    glColorMaterial( GL_FRONT, GL_AMBIENT_AND_DIFFUSE );

	glEnable(GL_LIGHT0);
	
	GLfloat ambientLight0[] = { 0.2f, 0.2f, 0.2f, 1.0f };
	GLfloat diffuseLight0[] = { 0.4f, 0.4f, 0.4f, 1.0f };
	GLfloat specularLight0[] = { 1.0f, 1.0f, 1.0f, 1.0f };
	GLfloat spotDirectionLight0[] = { 1.0f, 1.0f, 1.0f };
	GLfloat spotExponentLight0[] = { 0.0f };
	GLfloat constantAttenuationLight0[] = { 1.0f };
	GLfloat linearAttenuationLight0[] = { 1.0f };
	GLfloat quadraticAttenuationLight0[] = { 1.0f };
	glLightfv(GL_LIGHT0, GL_AMBIENT, ambientLight0);
	glLightfv(GL_LIGHT0, GL_DIFFUSE, diffuseLight0);
	glLightfv(GL_LIGHT0, GL_SPECULAR, specularLight0);
	glMateriali(GL_FRONT, GL_SHININESS, 128);
	//glLightfv(GL_LIGHT0, GL_SPOT_DIRECTION, spotDirectionLight0);
	//glLightfv(GL_LIGHT0, GL_SPOT_EXPONENT, spotExponentLight0);
	//glLightfv(GL_LIGHT0, GL_CONSTANT_ATTENUATION, constantAttenuationLight0);
	//glLightfv(GL_LIGHT0, GL_LINEAR_ATTENUATION,  linearAttenuationLight0);
	//glLightfv(GL_LIGHT0, GL_QUADRATIC_ATTENUATION, quadraticAttenuationLight0);
	
	GLfloat specularMaterial[] = { 1.0f, 1.0f, 1.0f, 1.0f };
	glMaterialfv(GL_FRONT, GL_SPECULAR, specularMaterial );

	//Vector3f cam_position( 0.0f, 0.0f, 0.0f );//cam.getFaceAxis() );
	//GLfloat positionLight0[] = { cam_position.x, cam_position.y, cam_position.z, 1.0f };
	//glLightfv(GL_LIGHT0, GL_POSITION, positionLight0);
	
	read_pts( "Horse.pts", horse_points );
}

// Setup CallBacks
void InitializeCallBacks()
{
    glutIgnoreKeyRepeat( false );    // ignore multiple key presses (i.e. holding onto keys only count once)
    glutIdleFunc( RenderScene );

    glutReshapeFunc( ChangeSize );
    glutDisplayFunc( RenderScene );
    
    // Handles the keyboard functionalities
    glutIgnoreKeyRepeat( true ); // nonzero indicates to ignore the repeat of keys
    glutSpecialFunc( SpecialKeysDown );
	glutSpecialUpFunc( SpecialKeysUp );
    glutKeyboardFunc( KeyboardHandler );
    glutKeyboardUpFunc( KeyboardUpHandler );
    
    // Handles the mouse functionalities
    glutEntryFunc( WindowEntry );
	glutMotionFunc( MouseMovePassive );  // motion callback for a window is called when the mouse moves within the window while one or more mouse buttons are pressed
    glutPassiveMotionFunc( MouseMovePassive ); // passive motion callback for a window is called when the mouse moves within the window while no mouse buttons are pressed
    glutMouseFunc( MouseClickHandler );

    //glutTimerFunc( 33, TimerFunction, 33 );
    glutTimerFunc( 500, TimerFunction, 500 );
    
    SetupRC();
}

// Full-screen mode setup
void enterFullScreenMode()
{
    if ( windowID > 0x0 )
        glutDestroyWindow( windowID );
    
    // Setting the game mode replaces the above
    // calls to set the window size and position.
    glutGameModeString(":32");   //glutGameModeString("800x600:32");
    // Enter into full screen mode
    if ( glutGameModeGet( GLUT_GAME_MODE_POSSIBLE ) ) 
        glutEnterGameMode();
    else 
    {
        printf("Game mode is not compatible with your system. Exiting program.\n");
		system("PAUSE");
        exit(1);	
    } 
	
    InitializeCallBacks();
}

// Contrasts with glutEnterGameMode (full-screen)
void enterWindowMode()
{
    glutInitWindowPosition(0, 0);
    glutInitWindowSize( windowWidth, windowHeight );
    windowID = glutCreateWindow( "Test" );
    
    InitializeCallBacks();
}

#undef CIRCLERX
#undef CIRCLERY
#undef CIRCLERZ

 #endif SCENE_MODULE_H